// License Notification
//======================================================
// Copyright (c) 2010 Channel Intelligence Inc.
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// which accompanies this distribution, and is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// ======================================================




using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using CIP4.Common;
using CIP4.Item;
using CIP4.DataProvider;
using CIP4.Simple;

namespace CIP4.DataProvider.Dictionary
{
    public class DataProvider : CIP4.Simple.DataProvider, IDataProvider
    {
        IDataProvider Cache { get; set; } 

        #region IDataProvider Members

        public string ConnectionString { get; private set; }

        public DataProvider (CIP4ConnectionAttribute DataProviderConnection, IDataProvider cachingProvider, LoggerDelegate logger)
            : base (DataProviderConnection)
        {
            Cache = cachingProvider;
            if (!(DataProviderConnection is CIP4TemplateConnectionAttribute))
                LoggerWrapper(MajorEnum.Error, MinorEnum.UnexpectedConnectionType,
                    "{0} is not an acceptable connection type", new object[] { DataProviderConnection.ToString() }, logger);
            else
            {
                LoggerWrapper(MajorEnum.Trace, MinorEnum.InitializingProvider,
                    "Initalizing Provider: {0}", new object[] { DataProviderConnection }, logger);
                CIP4TemplateConnectionAttribute connection = (CIP4TemplateConnectionAttribute)DataProviderConnection;
                ConnectionString = connection.ToString();
                throw new NotImplementedException("Pick up specific connection properties and validate environment");
            }
        }

        public void DeleteCollection(CollectionType collection, LoggerDelegate logger)
        {
            // Use CollectionLockType if your underlying store does not support concurrent accessors
            // it will use a system wide Mutex to ensure one acess-at-a-time to the store
            using (new CollectionLockType(collection.Name))
            {
                if (!LoggerWrapper(MajorEnum.Trace, MinorEnum.ActionStart,
                    "Started {0}", new object[] { MinorEnum.DeleteCollection }, logger))
                    return;

                throw new NotImplementedException("Delete the given collection");

                LoggerWrapper(MajorEnum.Information, MinorEnum.DeleteCollection,
                    "Collection deleted {0}", new object[] { collection }, logger);
            }
        }

        public IEnumerable<object> GetItems(CollectionType collection, IBooleanValue selectionExpression, CursorType cursor)
        {
            using (new CollectionLockType(collection.Name))
            {
                List<object> items = new List<object>();
                if (!LoggerWrapper(MajorEnum.Trace, MinorEnum.ActionStart,
                    "Started {0}", new object[] { MinorEnum.GetItems }, cursor.logger))
                    return items;

                try
                {
                    ISerializerType serializer = cursor.GetSerializer(SerializerKind);

                    string lookForExternalId = selectionExpression == null ? null : selectionExpression.ComparesTo("ExternalID").FirstOrDefault();

                    if (!string.IsNullOrEmpty(lookForExternalId))
                    {
                        throw new NotImplementedException("Do a search for a specific item using the ExternalID value");
                        //    string itemString = GetViaExternalId(lookForExternalId);
                        //    object item = serializer.GetObjectForData(XElement.Parse(itemString));
                        //    if (CompareProperties(selectionExpression, item, serializer))
                        //    {
                        //        items.Add(item);
                        //    }
                        return items;
                    }
                    else
                    {
                        if (cursor.FailOnLinearSearch)
                            LoggerWrapper(MajorEnum.Error, MinorEnum.FailOnLinearSearch,
                                "Failed on linear search accessing {0}", new object[] { collection }, cursor.logger);

                        
                            
                        throw new NotImplementedException("Do a search for all items that satisfy the selection expression (if any). Use the cursor.marker, cursor.PageNumber and cursor.PageSize properties to carry on from a previous page if necessary"); 
                        // Code will look something like:
                        //bool foundMarker = string.IsNullOrEmpty(cursor.Marker);
                        //foreach (string itemString in collectionDictionary.Values)
                        //{
                        //    object item = serializer.GetObjectForData(XElement.Parse(itemString));
                        //    if (foundMarker && CompareProperties(selectionExpression, item, serializer))
                        //    {
                        //        items.Add(item);
                        //        cursor.Marker = serializer.GetExternalID(item).ToString();
                        //    }
                        //    foundMarker = foundMarker || serializer.GetExternalID(item).ToString() == cursor.Marker;
                        //    if (items.Count == cursor.PageSize)
                        //        break;
                        //}
                    }
                    return items;
                }
                finally
                {
                    LoggerWrapper(MajorEnum.Information, MinorEnum.GetItems,
                        "Get {0} items from {1}", new object[] { items.Count, collection }, cursor.logger);
                }
            }
        }

        public void PurgeItems(IEnumerable<ExternalIDType> externalIDs, CollectionType collection, GetSerializerDelegate getSerializer)
        {
            using (new CollectionLockType(collection.Name))
            {
                ISerializerType serializer = getSerializer(SerializerKind);

                if (!LoggerWrapper(MajorEnum.Trace, MinorEnum.ActionStart,
                    "Started {0}", new object[] { MinorEnum.PurgeItems }, serializer.Logger))
                    return;

                int deletedCount = 0;

                throw new NotImplementedException("Delete the items from the store - keeping a count of how many you delete. If the store doesn't tell ypou, assume all the items in the list are deleted");

                if (deletedCount == 0)
                {
                    LoggerWrapper(MajorEnum.Warning, MinorEnum.NothingDeleted, "Nothing deleted from {0}", new object[] { collection.Name }, serializer.Logger);
                }
                LoggerWrapper(MajorEnum.Information, MinorEnum.PurgeItems,
                    "{0} items deleted from {1}", new object[] { deletedCount, collection.Name }, serializer.Logger);
            }
        }

        public void SaveItems(IEnumerable<object> items, CollectionType collection, GetSerializerDelegate getSerializer)
        {
            using (new CollectionLockType(collection.Name))
            {
                ISerializerType serializer = getSerializer(SerializerKind);

                if (!LoggerWrapper(MajorEnum.Trace, MinorEnum.ActionStart,
                    "Started {0}", new object[] { MinorEnum.SaveItems }, serializer.Logger))
                    return;

                int savedCount = 0;

                throw new NotImplementedException("Save all the items - keep a count");                
                // If the store gives an item-by-item save event, you can use the following to return informaion about the saves
                // LoggerWrapper(MajorEnum.Trace, MinorEnum.ItemsUpdated, "Items {0} updated", new object[] { externalID }, serializer.Logger);
                LoggerWrapper(MajorEnum.Information, MinorEnum.SaveItems,
                    "{0} items saved in {1}", new object[] { savedCount, collection.Name }, serializer.Logger);
            }
        }

        public void Terminate(LoggerDelegate logger)
        {
            // Do any tidy up here. The StrongWrapper will always call this for Save, Purge or DeleteCollection before returning to the user.
        }

        #endregion

    }
}
